package xstring

import (
	"bytes"
	"encoding/json"
	"fmt"
	"html/template"
	"strconv"
	"strings"
	"unicode"
	"unicode/utf8"
	"unsafe"

	"gitee.com/gousing/helper/internal"
	"golang.org/x/text/cases"
	"golang.org/x/text/language"
	"golang.org/x/text/width"
)

// FromAny 转换目标为字符串, 如果转换失败, 则返回空字符串
//   - copy gitee.com/gousing/values/ToStringE
func FromAny(v any) string {
	if v == nil {
		return ""
	}
	v = internal.UnderefVal(v)

	switch vs := v.(type) {
	case nil, struct{}, *struct{}:
		return ""
	case string:
		return vs
	case bool:
		if vs {
			return "true"
		}
		return "false"
	case float64:
		return strconv.FormatFloat(vs, 'f', -1, 64)
	case float32:
		return strconv.FormatFloat(float64(vs), 'f', -1, 32)
	case int:
		return strconv.Itoa(vs)
	case int64:
		return strconv.FormatInt(vs, 10)
	case int32:
		return strconv.Itoa(int(vs))
	case int16:
		return strconv.FormatInt(int64(vs), 10)
	case int8:
		return strconv.FormatInt(int64(vs), 10)
	case uint:
		return strconv.FormatUint(uint64(vs), 10)
	case uint64:
		return strconv.FormatUint(uint64(vs), 10)
	case uint32:
		return strconv.FormatUint(uint64(vs), 10)
	case uint16:
		return strconv.FormatUint(uint64(vs), 10)
	case uint8:
		return strconv.FormatUint(uint64(vs), 10)
	case json.Number:
		return vs.String()
	case []byte:
		return internal.BytesToString(vs)
	case template.HTML:
		return string(vs)
	case template.URL:
		return string(vs)
	case template.JS:
		return string(vs)
	case template.CSS:
		return string(vs)
	case template.HTMLAttr:
		return string(vs)
	case fmt.Stringer:
		return vs.String()
	case error:
		return vs.Error()
	case complex64:
		return fmt.Sprintf("(%g+%gi)", real(vs), imag(vs))
	case complex128:
		return fmt.Sprintf("(%g+%gi)", real(vs), imag(vs))
	default:
		if data, err := json.Marshal(vs); err == nil {
			return internal.BytesToString(data)
		}
		return ""
	}
}

// ToBytes 字符串转Bytes切片
//   - 在go版本<1.20中, 通过unsafe包和reflect包实现，其主要原理是拿到底层数组的指针，然后转换成[]byte或string。
//   - 在go版本>=1.20中 由于安全性问题reflect包中的StringHeader和SliceHeader已被标注为deprecated，建议使用unsafe包来实现转换。
//   - Use unsafe.String or unsafe.StringData instead.
//   - Use unsafe.Slice or unsafe.SliceData instead.
func ToBytes(data string) []byte {
	size := len(data)
	if size == 0 {
		return []byte{}
	}
	return unsafe.Slice(unsafe.StringData(data), size)
}

// FromBytes Bytes切片转字符串
func FromBytes(data []byte) string {
	size := len(data)
	if size == 0 {
		return ""
	}
	return unsafe.String(unsafe.SliceData(data), size)
}

// ToRunes 字符串转rune切片
func ToRunes(str string) []rune {
	if len(str) == 0 {
		return []rune{}
	}
	return internal.StringToRunes(str)
}

// FromRunes Runes切片转字符串
func FromRunes(data []rune) string {
	size := len(data)
	if size == 0 {
		return ""
	}
	var buf bytes.Buffer
	for i := range size {
		buf.WriteRune(data[i])
	}
	return buf.String()
}

// ToReverse 字符串反序
func ToReverse(str string) string {
	if len(str) < 1 {
		return str
	}
	b := ToRunes(str)
	for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {
		b[i], b[j] = b[j], b[i]
	}
	return FromRunes(b)
}

// ToDBC 半角字符串转全角字符串
func ToDBC(s string) string {
	return width.Widen.String(s)
}

// ToSBC 全角字符串转半角
func ToSBC(s string) string {
	return width.Narrow.String(s)
}

// ToLower 把字符转换为小写(支持UTF8字符串(含多字节字符,如中文))
func ToLower(str string) string {
	return cases.Lower(language.English).String(str)
}

// ToUpper 把字符转换为大写(支持UTF8字符串(含多字节字符,如中文))
func ToUpper(str string) string {
	return cases.Upper(language.English).String(str)
}

// UcFirst 把字符串中的首字符转换为大写
func UcFirst(str string) string {
	for i, v := range str {
		return string(unicode.ToUpper(v)) + str[i+1:]
	}
	return str
}

// LcFirst 把字符串中的首字符转换为小写
func LcFirst(str string) string {
	for i, v := range str {
		return string(unicode.ToLower(v)) + str[i+1:]
	}
	return str
}

// UcWords 把每个单词的首字符转换为大写
func UcWords(str string) string {
	return cases.Title(language.English).String(str)
}

// CamelToUnderline 驼峰式写法转为下划线写法
func CamelToUnderline(s string) string {
	if len(s) < 1 {
		return s
	}
	data := make([]byte, 0, len(s)*2)
	b := false //上一个字符是否是大写
	num := len(s)
	for i := 0; i < num; i++ {
		d := s[i]
		if d != '_' && (d < '0' || d > '9') && (d < 'a' || d > 'z') && (d < 'A' || d > 'Z') {
			//排除特殊字符
			continue
		}
		// or通过ASCII码进行大小写的转化
		// 65-90（A-Z），97-122（a-z）
		//判断如果字母为大写的A-Z就在前面拼接一个_
		if d >= 'A' && d <= 'Z' {
			if i == 0 {
				//首字符不处理
				data = append(data, d)
			} else if i == num-1 {
				//最后一个字符，且上一个字符是大写
				if !b {
					data = append(data, '_')
				}
				data = append(data, d)
			} else if i > 0 {
				//非首字符
				if b && i+1 < num && s[i+1] >= 'a' && s[i+1] <= 'z' {
					//上一个字符是大写, 且下一个字符是小写字母
					u := data[len(data)-1]
					data = data[:len(data)-1]
					data = append(data, u, '_')
					data = append(data, d)
				} else if !b {
					//上一个字符不是大写
					data = append(data, '_')
					data = append(data, d)
				} else if b && i+1 < num && (s[i+1] <= 'A' || s[i+1] >= 'Z') {
					//上一个字符是大写, 且下一个字符是不是大写
					data = append(data, d)
					data = append(data, '_')
				} else {
					data = append(data, d)
				}
			}
			b = true
		} else if i > 0 && d >= '0' && d <= '9' {
			//处理数字
			if s[i-1] < '0' || s[i-1] > '9' {
				//上一个字符不是数字
				data = append(data, '_')
				data = append(data, d)
			} else if i+1 < num && (s[i+1] < '0' || s[i+1] > '9') {
				//下一个字符不是数字
				data = append(data, d)
				data = append(data, '_')
			} else {
				data = append(data, d)
			}
			b = false
		} else {
			data = append(data, d)
			b = false
		}
	}
	//ToLower把大写字母统一转小写
	data = bytes.ToLower(data[:])
	data = bytes.ReplaceAll(data, []byte("___"), []byte("_"))
	data = bytes.ReplaceAll(data, []byte("__"), []byte("_"))
	return internal.BytesToString(bytes.Trim(data, "_"))
}

// UnderlineToCamel 下划线写法转为驼峰写法
func UnderlineToCamel(s string, bigCamel bool) string {
	data := make([]byte, 0, len(s))
	var (
		isAlphaLower bool
		isAlphaUpper bool
		nextUpper    bool
	)
	num := len(s) - 1
	for i := 0; i <= num; i++ {
		d := s[i]
		isAlphaLower, isAlphaUpper = false, false
		if d == '_' {
			// 下划线
			nextUpper = true
			continue
		} else if d >= 'a' && d <= 'z' {
			// 小写字母
			isAlphaLower = true
		} else if d >= 'A' && d <= 'Z' {
			// 大写字母
			isAlphaUpper = true
		} else if d > '0' && d <= '9' {
			// 数字
			data = append(data, d)
			continue
		} else {
			// 排除特殊字符
			continue
		}
		if len(data) == 0 {
			// 首字符
			if bigCamel {
				if isAlphaLower {
					d = d - 32
				}
			} else {
				if isAlphaUpper {
					d = d + 32
				}
			}
		} else if nextUpper {
			// 下划线后一个字符大写
			if isAlphaLower {
				d = d - 32
			}
			nextUpper = false
		}
		data = append(data, d)
	}
	data = bytes.ReplaceAll(data, []byte("_"), []byte(""))
	return internal.BytesToString(data)
}

// LenRune 计算UTF8字符串(含多字节字符,如中文)
// - 注意:无论ASCII字符串, 还是Rune字符(Unicode/UTF-8)均按1个字符长度计算
func LenRune(s string) int {
	return utf8.RuneCountInString(s)
}

// Sub 按字节方式截取字符串的一部分(支持多语言字符串)
//   - start：起始下标，>=0 时从头部开始截取, 负数从从尾部开始，最后一个为-1
//   - length：截取长度，<0 时表示截取到末尾
//   - 注意: 对于ASCII字符串, 1个字符占1个字节, 一个Rune(Unicode/UTF-8字符)可能占2个字节，3个字节，甚至4个字节
//   - 注意: 如字节区间包含不完整的字符，将做丢弃处理
func Sub(str string, start int, length int) string {
	bytesTotal := len(str)
	if bytesTotal == 0 || length == 0 {
		return ""
	}
	// 允许从尾部开始重新计算开始位置
	if start < 0 {
		start = bytesTotal + start
		if start < 0 {
			return ""
		}
	}
	if start > bytesTotal {
		return ""
	}
	// 处理负长度，表示截取到末尾
	if length < 0 {
		length = bytesTotal - start
		if length <= 0 {
			return ""
		}
	}
	end := start + length
	if end > bytesTotal {
		end = bytesTotal
	}
	if start == end {
		return ""
	}
	var builder strings.Builder
	for i, j := start, start; i < end; {
		//正确处理包含多字节字符, 避免截取到半个字符
		r, size := utf8.DecodeRuneInString(str[j:])
		if r == utf8.RuneError || size == 0 {
			j++
			if j >= end {
				break
			}
			continue
		}
		i += size
		j += size
		if i <= end {
			builder.WriteRune(r)
		}
	}
	return builder.String()
}

// SubRune 截取字符串的一部分(支持多语言字符串)
//   - start：起始下标，>=0 时从头部开始截取, 负数从从尾部开始，最后一个为-1
//   - length：截取长度，<0 时表示截取到末尾
//   - 注意: 无论ASCII字符串, 还是Rune字符(Unicode/UTF-8)均按1个字符长度计算
func SubRune(str string, start int, length int) string {
	if len(str) == 0 || length == 0 {
		return ""
	}
	runeSlice := ToRunes(str)
	runeTotal := len(runeSlice)
	if runeTotal == 0 {
		return ""
	}
	// 允许从尾部开始计算
	if start < 0 {
		start = runeTotal + start
		if start < 0 {
			return ""
		}
	}
	if start > runeTotal {
		return ""
	}
	// 处理负长度，表示截取到末尾
	if length < 0 {
		length = runeTotal - start
		if length <= 0 {
			return ""
		}
	}
	end := start + length
	if end > runeTotal {
		end = runeTotal
	}
	return internal.RunesToString(runeSlice[start:end])
}

// 截取左侧字符串，从右侧裁剪掉多余maxLen长度的字符串
//   - str 字符串
//   - maxLen 长度
//   - 返回 一个小于等于maxLen 长度的字符串
//   - 注意: 对于ASCII字符串, 1个字符占1个字节, 一个Rune(Unicode/UTF-8字符)可能占2个字节，3个字节，甚至4个字节
//   - 注意: 如字节区间包含不完整的字符，将做丢弃处理
func CutLeft(str string, maxLen int) string {
	if maxLen <= 0 {
		return ""
	} else if len(str) <= maxLen {
		return str
	}
	return Sub(str, 0, maxLen)
}

// 截取右侧字符串，从左侧裁剪掉多余maxLen长度的字符串
//   - str 字符串
//   - maxLen 长度
//   - 返回 一个小于等于maxLen 长度的字符串
//   - 注意: 对于ASCII字符串, 1个字符占1个字节, 一个Rune(Unicode/UTF-8字符)可能占2个字节，3个字节，甚至4个字节
//   - 注意: 如字节区间包含不完整的字符，将做丢弃处理
func CutRight(str string, maxLen int) string {
	if maxLen <= 0 {
		return ""
	} else if len(str) <= maxLen {
		return str
	}
	return Sub(str, -maxLen, maxLen)
}

// 截取左侧字符串(UTF8字符串)，从右侧裁剪掉多余maxLen长度的字符串
//   - str 字符串
//   - maxLen 长度
//   - 返回 一个小于等于maxLen 长度的字符串
//   - 注意: 无论ASCII字符串, 还是Rune字符(Unicode/UTF-8)均按1个字符长度计算
func CutLeftRune(str string, maxLen int) string {
	bytesTotal := len(str)
	if bytesTotal == 0 || maxLen <= 0 {
		return ""
	}
	if utf8.RuneCountInString(str) <= maxLen {
		return str
	}
	var cnt, end int
	for i := 0; i < bytesTotal; {
		_, size := utf8.DecodeRuneInString(str[i:])
		end += size
		i += size
		cnt++
		if cnt == maxLen {
			break
		}
	}
	return str[:end]
}

// 裁剪右侧字符串(UTF8字符串)，从左侧裁剪掉多余maxLen长度的字符串
//   - str 字符串
//   - maxLen 长度
//   - 返回 一个小于等于maxLen 长度的字符串
//   - 注意: 无论ASCII字符串, 还是Rune字符(Unicode/UTF-8)均按1个字符长度计算
func CutRightRune(str string, maxLen int) string {
	if len(str) == 0 || maxLen <= 0 {
		return ""
	}
	if utf8.RuneCountInString(str) <= maxLen {
		return str
	}

	// 初始化计数器和结束位置
	var cnt, start int = 0, len(str)
	// 从字符串末尾开始遍历
	for start >= 0 {
		_, size := utf8.DecodeLastRuneInString(str[:start])
		start -= size
		cnt++
		// 如果达到 maxLen，提前退出循环
		if cnt == maxLen {
			break
		}
	}

	// 返回截取后的字符串
	return str[start:]
}
